`timescale 1ns/1ps
`default_nettype none
/* NOTE:
* - 计算oe长度
*/

module calc_oe_len (
    // system signal
    input  wire         I_sclk,  // 125M
    input  wire         I_rst_n,
    // oe len
    input  wire         I_calc_req,
    output wire         O_calc_done,
    input  wire [15:0]  I_calc_max_len, // 最大长度
    input  wire [3:0]   I_calc_param0,  // 分母
    input  wire [5:0]   I_calc_param1,  // 分子
    output wire [15:0]  O_calc_result
);
//------------------------Parameter----------------------
// fsm
localparam [1:0]
    IDLE  = 0,
    PREP  = 1,
    SHIFT = 2,
    OVER  = 3;

//------------------------Local signal-------------------
reg  [1:0]  state;
reg  [1:0]  next;
reg  [14:0] tmp0;
reg  [14:0] tmp1;
reg  [15:0] result;
reg  [3:0]  cnt;

//------------------------Instantiation------------------

//------------------------Body---------------------------
assign O_calc_done   = (state == OVER);
assign O_calc_result = result;

// state
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        state <= IDLE;
    else
        state <= next;
end

// next
always @(*) begin
    case (state)
        IDLE: begin
            if (I_calc_req) begin
                if (I_calc_param0 == 1'b0)
                    next = OVER;
                else
                    next = PREP;
            end
            else
                next = IDLE;
        end

        // NOTE: 将分子扩展为15bit，bitN表示1/2^(15-N)
        PREP: begin
            if (cnt == 4'd15)
                next = SHIFT;
            else
                next = PREP;
        end

        SHIFT: begin
            if (cnt == 4'd15)
                next = OVER;
            else
                next = SHIFT;
        end

        OVER: begin
            next = IDLE;
        end

        default: begin
            next = IDLE;
        end
    endcase
end

// tmp0
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        tmp0 <= 1'b0;
    else if (state == IDLE)
        tmp0 <= I_calc_max_len[15:1];
    else if (state == SHIFT)
        tmp0 <= tmp0 >> 1;
end

// tmp1
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        tmp1 <= 1'b0;
    else if (state == IDLE)
        tmp1 <= I_calc_param1;
    else if (state == PREP && cnt != 4'd15)
        tmp1 <= tmp1 << 1;
    else if (state == SHIFT)
        tmp1 <= tmp1 << 1;
end

// result
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        result <= 1'b0;
    else if (state == IDLE && I_calc_req)
        result <= I_calc_max_len;
    else if (state == PREP)
        result <= 1'b0;
    else if (state == SHIFT && tmp1[14])
        result <= result + tmp0;
end

// cnt
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        cnt <= 1'b0;
    else if (state == IDLE)
        cnt <= I_calc_param0;
    else if (state == PREP && cnt == 4'd15)
        cnt <= 1'd1;
    else
        cnt <= cnt + 1'b1;
end

endmodule

`default_nettype wire

// vim:set ts=4 sw=4 et fenc=utf-8 fdm=marker:
